package com.phiture.erp.produce.core.service.impl;

import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
import cn.iocoder.yudao.framework.mybatis.core.util.MyBatisUtils;
import cn.iocoder.yudao.framework.mybatis.core.query.MPJLambdaWrapperX;
import cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils;
import cn.iocoder.yudao.framework.tenant.core.context.TenantContextHolder;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.phiture.erp.basic.api.pojo.resp.ErpUserCustomizeConfigResp;
import com.phiture.erp.basic.api.service.ErpMaterialApi;
import com.phiture.erp.basic.api.service.ErpUserCustomizeConfigApi;
import com.phiture.erp.common.constants.ErpNoPrefixConstants;
import com.phiture.erp.common.constants.ErpUserCustomizeConfigKeyConstants;
import com.phiture.erp.common.enums.ErpAuditStatus;
import com.phiture.erp.common.enums.ErpProduceInTypeEnum;
import com.phiture.erp.common.enums.ErpProductionTaskStatusEnum;
import com.phiture.erp.common.utils.ErpNoGenerator;
import com.phiture.erp.produce.common.enums.ErpOutExceedFlagEnum;
import com.phiture.erp.produce.core.convert.ErpProductionTaskMaterialConvertMapper;
import com.phiture.erp.produce.core.convert.ErpProductionTaskProcessesConvertMapper;
import com.phiture.erp.produce.core.pojo.dto.ErpProductionTaskPageReqDTO;
import com.phiture.erp.produce.core.pojo.dto.ErpProductionTaskReportDTO;
import com.phiture.erp.produce.core.pojo.dto.ErpProductionTaskSaveReqDTO;
import com.phiture.erp.produce.core.pojo.vo.ErpProduceInSaveReqVO;
import com.phiture.erp.produce.core.pojo.vo.ErpProductionTaskMaterialVO;
import com.phiture.erp.produce.core.pojo.vo.ErpProductionTaskProcessesVO;
import com.phiture.erp.produce.core.service.ErpProduceInService;
import com.phiture.erp.produce.core.service.ErpProductionTaskService;
import com.phiture.erp.produce.dal.entity.*;
import com.phiture.erp.produce.dal.mapper.ErpProductionTaskMapper;
import com.phiture.erp.produce.dal.mapper.ErpProductionTaskMaterialMapper;
import com.phiture.erp.produce.dal.mapper.ErpProductionTaskProcessesMapper;
import com.phiture.erp.produce.dal.pojo.vo.ErpProductionTaskRespVO;
import lombok.RequiredArgsConstructor;
import org.apache.ibatis.annotations.Param;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static com.phiture.erp.common.constants.ErrorCodeConstants.*;

/**
 * 生产任务 Service 实现类
 *
 * @author YH
 */
@Service
@RequiredArgsConstructor
public class ErpProductionTaskServiceImpl implements ErpProductionTaskService {
    private final ErpProductionTaskMapper erpProductionTaskMapper;
    private final ErpProductionTaskMaterialMapper erpProductionTaskMaterialMapper;
    private final ErpProductionTaskProcessesMapper erpProductionTaskProcessesMapper;
    private final ErpNoGenerator erpNoGenerator;
    private final ErpMaterialApi materialApi;
    private final ErpUserCustomizeConfigApi erpUserCustomizeConfigApi;

    private ErpProduceInService produceInService;

    @Resource
    public void setProduceInService(ErpProduceInService produceInService) {
        this.produceInService = produceInService;
    }
    @Override
    public ErpProductionTaskDO updateProductionTaskInCount(Long id, Map<Long, BigDecimal> inCountMap){
        ErpProductionTaskDO task = erpProductionTaskMapper.selectById(id);

        if (task == null) {
            return null;
        }
        // 更新入库数量
        BigDecimal inCount = inCountMap.getOrDefault(task.getId(), BigDecimal.ZERO);
//        if (task.getActualQuantity().equals(inCount)) {
//            return task;
//        }
//        if (inCount.compareTo(task.getPlannedQuantity()) > 0) {
//            throw exception(PRODUCTION_TASK_IN_FAIL_PRODUCT_EXCEED,
//                    materialApi.getMaterial(task.getMaterialId()).getName(), task.getPlannedQuantity());
//        }
        erpProductionTaskMapper.updateById(new ErpProductionTaskDO().setId(task.getId()).setActualQuantity(inCount));
        return task;
    }


    @Override
    @Transactional(rollbackFor = Exception.class)
    public Long createProductionTask(ErpProductionTaskSaveReqDTO createReqDTO) {
        // 插入
        ErpProductionTaskDO productionTask = BeanUtils.toBean(createReqDTO, ErpProductionTaskDO.class);
        String productionTaskNo = erpNoGenerator.generate(ErpNoPrefixConstants.PRODUCTION_TASK_NO_PREFIX);
        validateProductionTaskNo(productionTaskNo);
        productionTask.setProductionTaskNo(productionTaskNo);
        productionTask.setStatus(ErpProductionTaskStatusEnum.TO_BE_PRODUCED.getCode());
        erpProductionTaskMapper.insert(productionTask);

        // 插入子表
//        createProductionTaskMaterialList(productionTask.getId(), createReqDTO.getProductionTaskMaterials());
//        createProductionTaskProcessesList(productionTask.getId(), createReqDTO.getProductionTaskProcessess());
        // 返回
        return productionTask.getId();
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateProductionTask(ErpProductionTaskSaveReqDTO updateReqDTO) {
        // 校验存在
        validateProductionTaskExists(updateReqDTO.getId());
        // 更新
        ErpProductionTaskDO updateObj = BeanUtils.toBean(updateReqDTO, ErpProductionTaskDO.class);
        erpProductionTaskMapper.updateById(updateObj);

        // 更新子表
//        updateProductionTaskMaterialList(updateReqDTO.getId(), updateReqDTO.getProductionTaskMaterials());
//        updateProductionTaskProcessesList(updateReqDTO.getId(), updateReqDTO.getProductionTaskProcessess());
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void deleteProductionTask(Long id) {
        // 校验存在
        validateProductionTaskExists(id);
        // 删除
        erpProductionTaskMapper.deleteById(id);

        // 删除子表
//        deleteProductionTaskMaterialByProductionTaskId(id);
//        deleteProductionTaskProcessesByProductionTaskId(id);
    }

    private void validateProductionTaskExists(Long id) {
        if (erpProductionTaskMapper.selectById(id) == null) {
            throw exception(PRODUCTION_TASK_NOT_EXISTS);
        }
    }

    @Override
    public ErpProductionTaskDO getProductionTask(Long id) {
        return erpProductionTaskMapper.selectById(id);
    }

    @Override
    public PageResult<ErpProductionTaskRespVO> getProductionTaskPage(ErpProductionTaskPageReqDTO pageReqDTO) {
//        MPJLambdaWrapperX<ErpProductionTaskDO> queryWrapper = new MPJLambdaWrapperX<ErpProductionTaskDO>()
//                .eqIfPresent(ErpProductionTaskDO::getProductionTaskNo, pageReqDTO.getProductionTaskNo())
//                .eqIfPresent(ErpProductionTaskDO::getProductMainPlanNo, pageReqDTO.getProductMainPlanNo())
//                .eqIfPresent(ErpProductionTaskDO::getMaterialId, pageReqDTO.getMaterialId())
//                .eqIfPresent(ErpProductionTaskDO::getPriority, pageReqDTO.getPriority())
//                .eqIfPresent(ErpProductionTaskDO::getStatus, pageReqDTO.getStatus())
//                .betweenIfPresent(ErpProductionTaskDO::getStartDate, pageReqDTO.getStartDate())
//                .betweenIfPresent(ErpProductionTaskDO::getEndDate, pageReqDTO.getEndDate());
//
//        // 还需要左连接主计划表，根据主计划表的销售订单号进行模糊查询
//        if (pageReqDTO.getSaleOrderNo() != null) {
//            queryWrapper.leftJoin(ErpProduceMainPlanDO.class, ErpProduceMainPlanDO::getNo, ErpProductionTaskDO::getProductMainPlanNo)
//                    .like(ErpProduceMainPlanDO::getSourceBillNo, pageReqDTO.getSaleOrderNo());
//        }
//        if(pageReqDTO.getIsEnableIn() == 1){
//            //增加计划数量大于实际数量的条件
//            queryWrapper.apply("planned_quantity > actual_quantity");
//        }

        IPage<ErpProductionTaskRespVO> pageResult =
                erpProductionTaskMapper.selectMyPage(MyBatisUtils.buildPage(pageReqDTO),
                pageReqDTO.getProductionTaskNo(),
                pageReqDTO.getSaleOrderNo(),
                pageReqDTO.getMaterialName(),
                pageReqDTO.getMaterialId(),
                pageReqDTO.getStatus(),
                pageReqDTO.getIsEnableIn(),
                pageReqDTO.getStartDate(),
                pageReqDTO.getEndDate(),
                TenantContextHolder.getTenantId());
        return new PageResult<>(pageResult.getRecords(), pageResult.getTotal());
//        queryWrapper.orderByDesc(ErpProductionTaskDO::getId);
//        return erpProductionTaskMapper.selectPage(pageReqDTO, queryWrapper);
    }


    @Override
    public PageResult<ErpProductionTaskRespVO> getProductionTaskPageForMobile(ErpProductionTaskPageReqDTO pageReqDTO) {
        IPage<ErpProductionTaskRespVO> pageResult =
                erpProductionTaskMapper.selectMyPageForMobile(MyBatisUtils.buildPage(pageReqDTO),
                        pageReqDTO.getProductionTaskNo(),
                        pageReqDTO.getSaleOrderNo(),
                        pageReqDTO.getMaterialName(),
                        pageReqDTO.getStatus(),
                        TenantContextHolder.getTenantId(),
                        SecurityFrameworkUtils.getLoginUserId());
        return new PageResult<>(pageResult.getRecords(), pageResult.getTotal());
    }

    @Override
    public List<ErpProductionTaskMaterialVO> getProductionTaskMaterialListByProductionTaskId(Long productionTaskId, Long processId) {
        List<ErpProductionTaskMaterialDO> list = erpProductionTaskMaterialMapper.selectListByProductionTaskId(productionTaskId, processId);
        return ErpProductionTaskMaterialConvertMapper.INSTANCE.toErpProductionTaskMaterialVOList(list);
    }

    private void createProductionTaskMaterialList(Long productionTaskId, List<ErpProductionTaskMaterialDO> list) {
        list.forEach(o -> o.setProductionTaskId(productionTaskId));
        erpProductionTaskMaterialMapper.insertBatch(list);
    }

    private void deleteProductionTaskMaterialByProductionTaskId(Long productionTaskId) {
        erpProductionTaskMaterialMapper.deleteByProductionTaskId(productionTaskId);
    }

    // ==================== 子表（生产任务单工序） ====================

    @Override
    public List<ErpProductionTaskProcessesVO> getProductionTaskProcessesListByProductionTaskId(Long productionTaskId) {
        List<ErpProductionTaskProcessesDO> list = erpProductionTaskProcessesMapper.selectListByProductionTaskId(productionTaskId);
        return ErpProductionTaskProcessesConvertMapper.INSTANCE.toErpProductionTaskProcessesVOList(list);
    }

    @Override
    public void createErpProductionTaskFromMrp(ErpProductionTaskDO erpProductionTaskDO) {
        erpProductionTaskMapper.insert(erpProductionTaskDO);
    }

    @Override
    public void createErpProductionTaskMaterialFromMrp(List<ErpProductionTaskMaterialDO> taskMaterialList) {
        erpProductionTaskMaterialMapper.insertBatch(taskMaterialList);
    }

    @Override
    public void createErpProductionTaskProcessesFromMrp(List<ErpProductionTaskProcessesDO> taskProcessesList) {
        erpProductionTaskProcessesMapper.insertBatch(taskProcessesList);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean reportWork(ErpProductionTaskReportDTO reportDTO) {
        // 根据报工信息生成生产入库单

        // 1. 根据工序有无，判断是工序报工还是任务单报工
        if (reportDTO.getTaskProcessId() != null) {
            // 工序报工
            return reportWorkByProcess(reportDTO);
        } else {
            // 任务单报工
            return reportWorkByTask(reportDTO);
        }
    }

    private Boolean reportWorkByProcess(ErpProductionTaskReportDTO reportDTO) {
        // 1. 根据工序ID查询生产任务单工序
        ErpProductionTaskProcessesDO taskProcess = erpProductionTaskProcessesMapper.selectById(reportDTO.getTaskProcessId());
        if (taskProcess == null) {
            throw exception(PRODUCTION_PROCESS_NOT_EXISTS);
        }

        ErpUserCustomizeConfigResp erpStockOutReqCanExceed = erpUserCustomizeConfigApi.getUserCustomizeConfig(
                ErpUserCustomizeConfigKeyConstants.ERP_PRODUCE_TASK_REPORT_CAN_EXCEED);

        BigDecimal actualQuantityTotal = reportDTO.getActualQuantity().add(taskProcess.getCompletedQuantity() == null ? BigDecimal.ZERO : taskProcess.getCompletedQuantity());
        if(ErpOutExceedFlagEnum.CAN_NOT_EXCEED.getStatus().equals(erpStockOutReqCanExceed.getConfigValue())){
            // 检查 数据库数量+参数值是否大于等于物料需求数量
            if (actualQuantityTotal.compareTo(reportDTO.getPlannedQuantity()) > 0) {
                throw exception(PRODUCTION_TASK_IN_FAIL_PRODUCT_EXCEED,
                        materialApi.getMaterial(reportDTO.getMaterialId()).getName(), reportDTO.getPlannedQuantity());
            }
        }
        taskProcess.setCompletedQuantity(actualQuantityTotal);
        taskProcess.setStartDateActual(reportDTO.getStartDateActual());
        // 实际结束时间
        taskProcess.setEndDateActual(reportDTO.getEndDateActual());
        if (actualQuantityTotal.compareTo(reportDTO.getPlannedQuantity()) >= 0) {
            taskProcess.setStatus(ErpProductionTaskStatusEnum.COMPLETED.getCode());
        }else {
            taskProcess.setStatus(ErpProductionTaskStatusEnum.IN_PRODUCTION.getCode());
        }
        erpProductionTaskProcessesMapper.updateById(taskProcess);

        // 如果是最后一个工序，按生产任务报工逻辑进行
        if(taskProcess.getFinalProcess()){
            return reportWorkByTask(reportDTO);
        }
        return true;
    }

    private Boolean reportWorkByTask(ErpProductionTaskReportDTO reportDTO) {
        // 1. 根据生产任务单ID查询生产任务单
        ErpProductionTaskDO productionTask = erpProductionTaskMapper.selectById(reportDTO.getId());
        if (productionTask == null) {
            throw exception(PRODUCTION_TASK_NOT_EXISTS);
        }
        ErpUserCustomizeConfigResp erpStockOutReqCanExceed = erpUserCustomizeConfigApi.getUserCustomizeConfig(
                ErpUserCustomizeConfigKeyConstants.ERP_PRODUCE_TASK_REPORT_CAN_EXCEED);

        BigDecimal actualQuantityTotal = reportDTO.getActualQuantity().add(productionTask.getActualQuantity() == null ? BigDecimal.ZERO : productionTask.getActualQuantity());
        if(ErpOutExceedFlagEnum.CAN_NOT_EXCEED.getStatus().equals(erpStockOutReqCanExceed.getConfigValue())){
            // 检查 数据库数量+参数值是否大于等于物料需求数量
            if (actualQuantityTotal.compareTo(productionTask.getPlannedQuantity()) > 0) {
                throw exception(PRODUCTION_TASK_IN_FAIL_PRODUCT_EXCEED,
                        materialApi.getMaterial(productionTask.getMaterialId()).getName(), productionTask.getPlannedQuantity());
            }
        }
        // 2. 更新生产任务单的实际数量
        // productionTask.setActualQuantity(actualQuantity);   // 这里不需要更新实际数量，后续生产入库会反写
        // 初版不考虑下面这些字段，后续完善  TODO
        // 合格数量
//        BigDecimal qualifiedQuantity = reportDTO.getQualifiedQuantity().add(productionTask.getQualifiedQuantity() == null ? BigDecimal.ZERO : productionTask.getQualifiedQuantity());
//        productionTask.setQualifiedQuantity(qualifiedQuantity);
//        // 报废数量
//        BigDecimal scrapQuantity = reportDTO.getScrapQuantity().add(productionTask.getScrapQuantity() == null ? BigDecimal.ZERO : productionTask.getScrapQuantity());
//        productionTask.setScrapQuantity(scrapQuantity);
//         // 报废原因
//        productionTask.setScrapReason(reportDTO.getScrapReason());
        // 实际开始时间
        productionTask.setStartDateActual(reportDTO.getStartDateActual());
        // 实际结束时间
        productionTask.setEndDateActual(reportDTO.getEndDateActual());
        if (actualQuantityTotal.compareTo(productionTask.getPlannedQuantity()) >= 0) {
            productionTask.setStatus(ErpProductionTaskStatusEnum.COMPLETED.getCode());
        }else {
            productionTask.setStatus(ErpProductionTaskStatusEnum.IN_PRODUCTION.getCode());
        }
        erpProductionTaskMapper.updateById(productionTask);

        // 4. 生成生产入库单
        produceInService.createProduceIn(buildProductionInSaveReqVO(reportDTO));

        return true;
    }



    private ErpProduceInSaveReqVO buildProductionInSaveReqVO(ErpProductionTaskReportDTO reportDTO){

        ErpProduceInSaveReqVO productionInSaveReqVO = new ErpProduceInSaveReqVO();
        // 入库类型
        productionInSaveReqVO.setType(ErpProduceInTypeEnum.PRODUCTION.getType());
        // 状态
        productionInSaveReqVO.setStatus(ErpAuditStatus.PROCESS.getStatus());
        // 业务日期设置成当前时间
        productionInSaveReqVO.setBizDate(LocalDateTime.now());
        // 源单单号
        productionInSaveReqVO.setSourceBillNo(reportDTO.getProductionTaskNo());
        // 生产入库明细列表
        List<ErpProduceInItemsDO> produceInItemss = new ArrayList<>();

        ErpProduceInItemsDO erpProduceInItemsDO = new ErpProduceInItemsDO();
        // 物料ID
        erpProduceInItemsDO.setMaterialId(reportDTO.getMaterialId());
        // 入库数量
        erpProduceInItemsDO.setQty(reportDTO.getActualQuantity());
        // 最终确认数量
        erpProduceInItemsDO.setFinalQty(reportDTO.getActualQuantity());
        // 源单单号
        erpProduceInItemsDO.setSourceBillNo(reportDTO.getProductionTaskNo());
        // 源单ID
        erpProduceInItemsDO.setSourceBillId(reportDTO.getId());
        // 单位
        erpProduceInItemsDO.setUnitId(materialApi.getMaterial(reportDTO.getMaterialId()).getUnitId());
        produceInItemss.add(erpProduceInItemsDO);
        productionInSaveReqVO.setProduceInItemss(produceInItemss);

        return productionInSaveReqVO;
    }


    private void createProductionTaskProcessesList(Long productionTaskId, List<ErpProductionTaskProcessesDO> list) {
        list.forEach(o -> o.setProductionTaskId(productionTaskId));
        erpProductionTaskProcessesMapper.insertBatch(list);
    }

    private void deleteProductionTaskProcessesByProductionTaskId(Long productionTaskId) {
        erpProductionTaskProcessesMapper.deleteByProductionTaskId(productionTaskId);
    }

    private void validateProductionTaskNo(String productionTaskNo) {
        boolean exists = erpProductionTaskMapper.exists(
                Wrappers.lambdaQuery(ErpProductionTaskDO.class)
                        .eq(ErpProductionTaskDO::getDeleted, Boolean.FALSE)
                        .eq(ErpProductionTaskDO::getProductionTaskNo, productionTaskNo)
        );
        if(exists){
            throw exception(PRODUCTION_TASK_NO_EXISTS);
        }
    }



}